/*
 * getopt_long() -- 长选项解析器
 *
 * Portions Copyright (c) 1987, 1993, 1994
 * The Regents of the University of California.  All rights reserved.
 *
 * Portions Copyright (c) 2003
 * PostgreSQL Global Development Group
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *	  may be used to endorse or promote products derived from this software
 *	  without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * src/port/getopt_long.c
 */

#include "c.h"

#include "getopt_long.h"

#define BADCH	'?'
#define BADARG	':'
#define EMSG	""


/*
 * getopt_long
 *	解析 argc/argv 参数向量，支持长选项。
 *
 * 该实现不使用 optreset。相反，我们保证在调用返回 -1 后，
 * 如果调用者在新系列的第一次调用之前将 optind 重置为 1，
 * 它可以在新的 argv 数组上重新启动。 
 * （在内部，这意味着我们必须确保在返回 -1 之前将 "place" 重置为 EMSG。）
 */
int getopt_long(int fc_argc, char *const fc_argv[],
			const char *fc_optstring,
			const struct option *fc_longopts, int *fc_longindex)
{
	static char *fc_place = EMSG;	/* 选项字母处理 */
	char	   *fc_oli;			/* 选项字母列表索引 */

	if (!*fc_place)
	{							/* 更新扫描指针 */
		if (optind >= fc_argc)
		{
			fc_place = EMSG;
			return -1;
		}

		fc_place = fc_argv[optind];

		if (fc_place[0] != '-')
		{
			fc_place = EMSG;
			return -1;
		}

		fc_place++;

		if (!*fc_place)
		{
			/* 将 "-" 视为不选项 */
			fc_place = EMSG;
			return -1;
		}

		if (fc_place[0] == '-' && fc_place[1] == '\0')
		{
			/* 找到 "--"，将其视为选项结束 */
			++optind;
			fc_place = EMSG;
			return -1;
		}

		if (fc_place[0] == '-' && fc_place[1])
		{
			/* 长选项 */
			size_t		fc_namelen;
			int			fc_i;

			fc_place++;

			fc_namelen = strcspn(fc_place, "=");
			for (fc_i = 0; fc_longopts[fc_i].name != NULL; fc_i++)
			{
				if (strlen(fc_longopts[fc_i].name) == fc_namelen
					&& strncmp(fc_place, fc_longopts[fc_i].name, fc_namelen) == 0)
				{
					int			fc_has_arg = fc_longopts[fc_i].has_arg;

					if (fc_has_arg != no_argument)
					{
						if (fc_place[fc_namelen] == '=')
							optarg = fc_place + fc_namelen + 1;
						else if (optind < fc_argc - 1 &&
								 fc_has_arg == required_argument)
						{
							optind++;
							optarg = fc_argv[optind];
						}
						else
						{
							if (fc_optstring[0] == ':')
								return BADARG;

							if (opterr && fc_has_arg == required_argument)
								fprintf(stderr,
										"%s: option requires an argument -- %s\n",
										fc_argv[0], fc_place);

							fc_place = EMSG;
							optind++;

							if (fc_has_arg == required_argument)
								return BADCH;
							optarg = NULL;
						}
					}
					else
					{
						optarg = NULL;
						if (fc_place[fc_namelen] != 0)
						{
							/* XXX 错误？ */
						}
					}

					optind++;

					if (fc_longindex)
						*fc_longindex = fc_i;

					fc_place = EMSG;

					if (fc_longopts[fc_i].flag == NULL)
						return fc_longopts[fc_i].val;
					else
					{
						*fc_longopts[fc_i].flag = fc_longopts[fc_i].val;
						return 0;
					}
				}
			}

			if (opterr && fc_optstring[0] != ':')
				fprintf(stderr,
						"%s: illegal option -- %s\n", fc_argv[0], fc_place);
			fc_place = EMSG;
			optind++;
			return BADCH;
		}
	}

	/* 短选项 */
	optopt = (int) *fc_place++;

	fc_oli = strchr(fc_optstring, optopt);
	if (!fc_oli)
	{
		if (!*fc_place)
			++optind;
		if (opterr && *fc_optstring != ':')
			fprintf(stderr,
					"%s: illegal option -- %c\n", fc_argv[0], optopt);
		return BADCH;
	}

	if (fc_oli[1] != ':')
	{							/* 不需要参数 */
		optarg = NULL;
		if (!*fc_place)
			++optind;
	}
	else
	{							/* 需要一个参数 */
		if (*fc_place)				/* 无空格 */
			optarg = fc_place;
		else if (fc_argc <= ++optind)
		{						/* 无参数 */
			fc_place = EMSG;
			if (*fc_optstring == ':')
				return BADARG;
			if (opterr)
				fprintf(stderr,
						"%s: option requires an argument -- %c\n",
						fc_argv[0], optopt);
			return BADCH;
		}
		else
			/* 空格 */
			optarg = fc_argv[optind];
		fc_place = EMSG;
		++optind;
	}
	return optopt;
}
